package ar.com.e2solver.solver.operator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import ar.com.e2solver.exception.BusinessException;
import ar.com.e2solver.model.Coordinate;
import ar.com.e2solver.model.Piece;
import ar.com.e2solver.model.Puzzle;
import ar.com.e2solver.solver.Individual;
import ar.com.e2solver.solver.RandomConstants;

public class RegionRotateMutationOperator implements MutationOperator {

	private static RegionRotateMutationOperator instance = new RegionRotateMutationOperator();
	
	private RegionRotateMutationOperator(){}
	
	public static RegionRotateMutationOperator getInstance() {
		return instance;
	}
	
	public Individual operate(Individual individual) throws BusinessException {
		Puzzle result = new Puzzle(individual.getPuzzle());
		
		/*List<Coordinate> regionToExchange = getRegionToExchange(result.getRowsNumber(), result.getColumnNumbers());
		
		for (Iterator<Coordinate> iterator = regionToExchange.iterator(); iterator.hasNext();) {
			Coordinate coordinate = iterator.next();
			Coordinate toSwap = result.findCoordinateForPiece(p2.getPieceAt(coordinate));
			
			if(result.canSwap(coordinate, toSwap)){
				result.swapPositions(coordinate, toSwap);
				
				Piece piece1 = result.getPieceAt(coordinate);
				Piece piece2 = result.getPieceAt(toSwap);
				
				if(piece1.isABorderPiece()){
					rotateForBorder(coordinate.getX(), coordinate.getY(), piece1);
					rotateForBorder(toSwap.getX(), toSwap.getY(), piece2);
				} else if(piece1.isACornerPiece()) {
					rotateForCorner(coordinate.getX(), coordinate.getY(), piece1);
					rotateForCorner(toSwap.getX(), toSwap.getY(), piece2);
				}
			}
		}*/
		
		return new Individual(result);
	}

	private List<Coordinate> getRegionToExchange(int rows, int cols) {
		List<Coordinate> region = new ArrayList<Coordinate>();
		
		// Busca coordenada inicial. Elige cualquier (x, y) menos de la ultima fila y ultima columna
		Coordinate initialCoordinate = RandomConstants.getRandomCoordinate(rows - 1, cols - 1);
		// Busca la coordenada final de la region
		Coordinate finalCoordinate = getFinalCoordinate(initialCoordinate, rows, cols);
		
		for(int i = initialCoordinate.getX(); i <= finalCoordinate.getX(); i++){
			for(int j = initialCoordinate.getY(); j <= finalCoordinate.getY(); j++){
				region.add(new Coordinate(i, j));
			}
		}
		
		return region;
	}

	/**
	 * Busca la coordenada final de la region. La coordenada final tiene 
	 * que estar en el minimo entre el final de rompecabezas y 8.
	 * 
	 * @param initialCoordinate
	 * @return
	 */
	private Coordinate getFinalCoordinate(Coordinate initialCoordinate, int rows, int cols) {
		int remainingRows = rows - (initialCoordinate.getX() + 1);
		int remainingCols = cols - (initialCoordinate.getY() + 1);
		
		int regionRemainingRows = Math.min(remainingRows, 8);
		int regionRemainingCols = Math.min(remainingCols, 8);
		
		int rowsToAdd = RandomConstants.RANDOM.nextInt(regionRemainingRows) + 1;
		int colsToAdd = RandomConstants.RANDOM.nextInt(regionRemainingCols) + 1;
		
		return new Coordinate(initialCoordinate.getX() + rowsToAdd, initialCoordinate.getY() + colsToAdd);
	}
	
	private void rotateForBorder(int i, int j, Piece piece) {
		if(i == 0 ){
			while(!piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
			
		} else if(i > j){
			while(!piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
			
		} else {
			while(!piece.getRight().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}	
		}
	}

	private void rotateForCorner(int i, int j, Piece piece) {
		if(i == 0 && j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR) || !piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(i == 0 && j != 0){
			while(!piece.getRight().equals(Piece.BORDER_COLOR) || !piece.getTop().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}
		} else if(i != 0 && j == 0){
			while(!piece.getLeft().equals(Piece.BORDER_COLOR) || !piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}			
		} else {
			while(!piece.getRight().equals(Piece.BORDER_COLOR) || !piece.getBottom().equals(Piece.BORDER_COLOR)){
				piece.rotateToRight(1);
			}			
		}
	}

}
